﻿using System;

namespace CSharpConsoleTestSolution.Demo {
    /**
     * 事件的声明
     *   ·完整声明
     *   ·简略声明(字段式声明，field-like )
     * 
     * 有了委托字段/属性，为什么还需要事件 ?
     *   ·为了程序的逻辑更加“有道理”、更加安全，谨防“借刀杀人”
     *
     * 所以事件的本质是委托字段的一个包装器
     *   ·这个包装器对委托字段的访问起限制作用，相当于一个“蒙板"
     *   ·封装(encapsulation)的一个重要功能就是隐藏
     *   ·事件对外界隐藏了委托实例的大部分功能，仅暴露添加/移除事件处理器的功能
     *   ·添加/移除事件处理器的时候可以直接使用方法名，这是委托实例所不具备的功能
     * 
     * 用于声明事件的委托类型的命名约定
     *   ·用于声明Foo事件的委托，一般命名为FooEventHandler( 除非是一个非常通用的事件约束)
     *   ·FooEventHandler委托的参数一般有两个(由Win32 API演化而来，历史悠久)
     *      ·第一个是object类型，名字为sender，实际上就是事件的拥有者、事件的source。
     *      ·第二个是EventArqs类的派生类，类名一般为FooEventArgs，参数名为e。也就是前面讲过的事件参数
     *      ·虽然没有官方的说法，但我们可以把委托的参数列表看做是事件发生后发送给事件响应者的“事件消息"
     *   ·触发Foo事件的方法一般命名为OnFoo，即“因何引发”、“事出有因“
     *      ·访问级别为protected，不能为public，不然又成了可以"借刀杀人“了
     *
     * 事件的命名约定
     *   ·带有时态的动词或者动词短语
     *   ·事件拥有者”正在做”什么事情用进行时;事件拥有者“做完了”什么事情，用完成时
     */
    public class P22_LinQ {

        private class OrderEventArgs: EventArgs {   //: EventArgs 非必须, 约定成俗
            public string DishName{ get; set; }
            public int Size{ get; set; }
        }
        private delegate void OrderEventHandler(Customer sender, OrderEventArgs e);
        
        private class Customer {
            private OrderEventHandler _handler;
            //自定义事件, 加上关键字: event
            public event OrderEventHandler Order {
                add => _handler += value;
                remove => _handler -= value;
            }
            public double Bill { get; set; }
            public void PayTheBill() {
                Console.WriteLine($"I will pay ${Bill}.");
            }
            public void Book() {
                if (_handler == null) return;
                var orderEventArgs = new OrderEventArgs { DishName = "Kongpao Chicken", Size = 2 };
                _handler.Invoke(this, orderEventArgs);
            }
        }
        
        private class Waiter {
            public void Action(Customer sender, OrderEventArgs e) {
                Console.WriteLine($"I will server you the dish: {e.DishName} x {e.Size}");
                double price = 10 * e.Size; //单价10元
                sender.Bill += price;
            }
        }
        
        /**
         * 1.事件的完整声明
         */
        public static void event_complete_statement() {
            Console.WriteLine("1.事件的完整声明");
            Console.WriteLine("private class OrderEventArgs: EventArgs {   //: EventArgs 非必须, 约定成俗");
            Console.WriteLine("    public string DishName{ get; set; }");
            Console.WriteLine("    public int Size{ get; set; }");
            Console.WriteLine("}");
            Console.WriteLine("private delegate void OrderEventHandler(Customer sender, OrderEventArgs e);");
            Console.WriteLine("private class Customer {");
            Console.WriteLine("    private OrderEventHandler _handler;");
            Console.WriteLine("    //自定义事件, 加上关键字: event");
            Console.WriteLine("    public event OrderEventHandler Order {");
            Console.WriteLine("        add => _handler += value;");
            Console.WriteLine("        remove => _handler -= value;");
            Console.WriteLine("    }");
            Console.WriteLine("    public double Bill { get; set; }");
            Console.WriteLine("    public void PayTheBill() {");
            Console.WriteLine("        Console.WriteLine($\"I will pay ${Bill}.\");");
            Console.WriteLine("    }");
            Console.WriteLine("    public void Book() {");
            Console.WriteLine("        if (_handler == null) return;");
            Console.WriteLine("        var orderEventArgs = new OrderEventArgs { DishName = \"Kongpao Chicken\", Size = 2 };");
            Console.WriteLine("        _handler.Invoke(this, orderEventArgs);");
            Console.WriteLine("    }");
            Console.WriteLine("}");
            Console.WriteLine();
            Console.WriteLine("private class Waiter {");
            Console.WriteLine("    public void Action(Customer sender, OrderEventArgs e) {");
            Console.WriteLine("        Console.WriteLine($\"I will server you the dish: {e.DishName} x {e.Size}\");");
            Console.WriteLine("        double price = 10 * e.Size; //单价10元");
            Console.WriteLine("        sender.Bill += price;");
            Console.WriteLine("    }");
            Console.WriteLine("}");
            
            Console.WriteLine();
            Console.WriteLine("var customer = new Customer();");
            Console.WriteLine("var waiter = new Waiter();");
            Console.WriteLine("customer.Order += waiter.Action;");
            Console.WriteLine("customer.Book();");
            Console.WriteLine("customer.PayTheBill();");
            var customer = new Customer();
            var waiter = new Waiter();
            customer.Order += waiter.Action;
            customer.Book();
            customer.PayTheBill();
        }
        
        
        
        private class Customer_Brief: Customer {
            //自定义事件, 加上关键字: event
            public event OrderEventHandler Order;
            //也可以调用, 但是这是变量, 可以被外部调用 Order2.Invoke(), 不安全, 见视频
            public /*event*/ OrderEventHandler Order2;
            public void Book() {
                if (Order == null) return;
                var orderEventArgs = new OrderEventArgs { DishName = "ZuoZongTang Chicken", Size = 2 };
                Order.Invoke(this, orderEventArgs);
            }
        }

        /**
         * 2.简略声明(字段式声明，field-like ), 时间: 17:40
         */
        public static void event_brief_statement() {
            Console.WriteLine();
            Console.WriteLine("2.简略声明(字段式声明，field-like )");
            Console.WriteLine("private class Customer_Brief {");
            Console.WriteLine("    //自定义事件, 简略声明(字段式声明，field-like )");
            Console.WriteLine("    public event OrderEventHandler Order;");
            Console.WriteLine("    public double Bill { get; set; }");
            Console.WriteLine("    public void PayTheBill() {");
            Console.WriteLine("        Console.WriteLine($\"I will pay ${Bill}.\");");
            Console.WriteLine("    }");
            Console.WriteLine("    public void Book() {");
            Console.WriteLine("        if (Order == null) return;");
            Console.WriteLine("        var orderEventArgs = new OrderEventArgs { DishName = \"Kongpao Chicken\", Size = 2 };");
            Console.WriteLine("        Order.Invoke(this, orderEventArgs);");
            Console.WriteLine("    }");
            Console.WriteLine("}");
            
            Console.WriteLine();
            Console.WriteLine("var customer_brief = new Customer_Brief();");
            Console.WriteLine("var waiter = new Waiter();");
            Console.WriteLine("customer_brief.Order += waiter.Action;");
            Console.WriteLine("customer_brief.Book();");
            Console.WriteLine("customer_brief.PayTheBill();");
            var customer_brief = new Customer_Brief();
            var waiter = new Waiter();
            customer_brief.Order += waiter.Action;
            customer_brief.Book();
            customer_brief.PayTheBill();
        }

        /**
         * 3.有了委托字段/属性，为什么还需要事件 ?, 时间: 33:15
         * 为了程序的逻辑更加“有道理”、更加安全，谨防“借刀杀人”
         * 
         *  //也可以调用, 但是这是变量, 可以被外部调用 Order2.Invoke(), 不安全, 见视频
         *  public /*event* / OrderEventHandler Order2;
         */
        public static void notice() {
            Console.WriteLine();
            Console.WriteLine("3.有了委托字段/属性，为什么还需要事件 ?");
            Console.WriteLine("为了程序的逻辑更加“有道理”、更加安全，谨防“借刀杀人”");
            Console.WriteLine();
            Console.WriteLine("//也可以调用, 但是这是变量, 可以被外部调用 Order2.Invoke(), 不安全, 见视频");
            Console.WriteLine("public /*event*/ OrderEventHandler Order2;");
            Console.WriteLine();
            Console.WriteLine("在Customer类内部能够使用Order事件去做非空比较, 以及调用 Order.Invoke() 方法纯属不得已而为之，");
            Console.WriteLine("因为使用事件的简化声明时，我们没有手动声明一个委托类型的字段。这是微软编译器语法糖所造成的语法冲突和前后不一致。");
            Console.WriteLine("if (Order == null) return;");
            Console.WriteLine("Order.Invoke();");
        }

        
        
        private class Customer_EventHandler: Customer {
            //自定义事件, 加上关键字: event
            public event EventHandler Order;
            public void Book() {
                if (Order == null) return;
                var orderEventArgs = new OrderEventArgs { DishName = "Chicken", Size = 2 };
                Order.Invoke(this, orderEventArgs);
            }
        }
        private class Waiter_EventHandler {
            public void Action(object sender, EventArgs e) {
                Customer_EventHandler customer = sender as Customer_EventHandler;
                OrderEventArgs eventArgs = e as OrderEventArgs;
                Console.WriteLine($"I will server you the dish: {eventArgs.DishName} x {eventArgs.Size}");
                double price = 10 * eventArgs.Size; //单价10元
                customer.Bill += price;
            }
        }

        /**
         * 4.EventHandler 委托, 时间: 43:35
         */
        public static void EventHandler_delegate() {
            Console.WriteLine();
            Console.WriteLine("4.EventHandler 委托");
            Console.WriteLine("private class Waiter_EventHandler {");
            Console.WriteLine("    public void Action(object sender, EventArgs e) {");
            Console.WriteLine("        //下方2句, 需要强转一下...");
            Console.WriteLine("        OrderEventArgs eventArgs = e as OrderEventArgs;");
            Console.WriteLine("        Console.WriteLine($\"I will server you the dish: {eventArgs.DishName} x {eventArgs.Size}\");");
            Console.WriteLine("        double price = 10 * eventArgs.Size; //单价10元");
            Console.WriteLine("        customer.Bill += price;");
            Console.WriteLine("   }");
            Console.WriteLine("}");
            
            Console.WriteLine();
            Console.WriteLine("var customer = new Customer_EventHandler();");
            Console.WriteLine("var waiter = new Waiter_EventHandler();");
            Console.WriteLine("customer.Order += waiter.Action;");
            Console.WriteLine("customer.Book();");
            Console.WriteLine("customer.PayTheBill();");
            var customer = new Customer_EventHandler();
            var waiter = new Waiter_EventHandler();
            customer.Order += waiter.Action;
            customer.Book();
            customer.PayTheBill();
        }

        /**
         * 5.事件 与 委托 的关系, 时间: 57:06
         * 事与委托的关系事件真的是“以特殊方式声明的委托字段/实例”吗?
         *   ·不是! 只是声明的时候”看起来像”( 对比委托字段与事件的简化声明，field-like )
         *   ·事件声明的时候使用了委托类型，简化声明造成事件看上去像一个委托的字段(实例)，而event关键字则更像是一个修饰符 ━━ 这就是错觉的来源之-
         *   ·订阅事件的时候+=操作符后面可以是一个委托实例，这与委托实例的赋值方法语法相同，这也让事件看起来像是一个委托字段 ━━ 这是错觉的又一来源
         *   ·重申: 事件的本质是加装在委托字段上的一个”蒙板”( mask )，是个起掩蔽作用的包装器。这个用于阻挡非法操作的”蒙板”绝不是委托字段本身
         *
         * 为什么要使用委托类型来声明事件 ?
         *   ·站在source的角度来看，是为了表明source能对外传递哪些消息
         *   ·站在subscriber的角度来看，它是一种约定，是为了约束能够使用什么样签名的方法来处理(响应)事件
         *   ·委托类型的实例将用于存储(引用)事件处理器
         *
         * 对比事件与属性
         *   ·属性不是字段 ━━ 很多时候属性是字段的包装器，这个包装器用来保护字段不被滥用
         *   ·事件不是委托字段 ━━ 它是委托字段的包装器，这个包装器用来保护委托字段不被滥用
         *   ·包装器永远都不可能是被包装的东西
         */
        public static void event_delegate_relationship() {
            Console.WriteLine();
            Console.WriteLine("5.━━━━━━━━━━━━━━━━━━");
            Console.WriteLine("EventHandler 感觉不好用(Waiter.Action()方法里还需要强转), 感觉下面的声明方式更好用一些:");
            Console.WriteLine("public event Action<Customer_EventHandler, OrderEventArgs> Order; //Customer里面定义");
            Console.WriteLine("Order.Invoke(this, orderEventArgs); //Customer里面调用");
            Console.WriteLine("customer.Order += waiter.Action; //主方法里面给Customer里的Order赋值");
        }

    }
}